#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define PLATFORM_CNT        1			/* 设备号个数 	*/
#define PLATFORM_NAME       "dtsplatled"	/* 名字 		*/
#define LEDOFF              0X00		/* 关灯 	*/
#define LEDON               0X01		/* 开灯 	*/


struct newchrled_dev{
    dev_t devid;				/* 设备号 		*/
    struct cdev cdev;			/* cdev 		*/
    struct class *class;		/* 类 		*/
    struct device *device;		/* 设备 		*/
    int led_gpio;				/* led的GPIO编号 */
    int major;					/* 主设备号 	*/
    int minor;					/* 次设备号 	*/
    struct device_node *nd;		/* 设备节点 	*/
};
struct newchrled_dev newchrled;	/* led设备 */


static int newchrled_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &newchrled; /* 设置私有数据 */
    return 0;
}
static ssize_t newchrled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    int retvalue;
    unsigned char databuf[1];
    unsigned char ledstat;
    retvalue = copy_from_user(databuf, buf, count);
    if(retvalue < 0) {
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }
    ledstat = databuf[0];
    if (ledstat == LEDON) {
        gpio_set_value(newchrled.led_gpio, 0);
    } else if (ledstat == LEDOFF) {
        gpio_set_value(newchrled.led_gpio, 1);
    }
    return 0;
}
static struct file_operations newchrled_fops = {
    .owner = THIS_MODULE,
    .open = newchrled_open,
    .write = newchrled_write,
};
static int led_init(struct platform_device *dev)
{
    printk("led init runing\r\n");
    int ret = 0;
    /* 1、获取设备节点 */
    newchrled.nd = of_find_node_by_path("/gpioled");
    // newchrled.nd = dev->dev.of_node;  //这里有点问题，，，，
    if(newchrled.nd == NULL) {
        printk("gpioled node not find!\r\n");
        return -EINVAL;
    }
    /* 2、获取led的GPIO编号 */
    newchrled.led_gpio = of_get_named_gpio(newchrled.nd, "led-gpios", 0);
    if(newchrled.led_gpio < 0) {
        printk("can't get led-gpios");
        return -EINVAL;
    }
    /* 3、设置GPIO1_IO03为输出，并且输出高电平，默认打开LED */
    ret = gpio_direction_output(newchrled.led_gpio, 0);
    if(ret < 0) {
        printk("can't set gpio!\r\n");
        goto fail_out_gpio;
    }
fail_out_gpio:
    gpio_free(newchrled.led_gpio);

    return 0;
}
static int led_probe(struct platform_device *dev)
{
    int ret = 0;
    // ret = led_init(dev);
    if (ret < 0)
    {
        return -EFAULT;
    }
    /* 1、创建设备号 */
    if (newchrled.major) {		/*  定义了设备号 */
        newchrled.devid = MKDEV(newchrled.major, 0);
        ret = register_chrdev_region(newchrled.devid, PLATFORM_CNT, PLATFORM_NAME);
    } else {						/* 没有定义设备号 */
        ret = alloc_chrdev_region(&newchrled.devid, 0, PLATFORM_CNT, PLATFORM_NAME);	/* 申请设备号 */
        newchrled.major = MAJOR(newchrled.devid);	/* 获取分配号的主设备号 */
        newchrled.minor = MINOR(newchrled.devid);	/* 获取分配号的次设备号 */
    }
    printk("newcheled major=%d,minor=%d\r\n",newchrled.major, newchrled.minor);
    if (ret < 0) {
        ret = -ENAVAIL;
        goto fail_error;
    }
    /* 2、初始化cdev */
    newchrled.cdev.owner = THIS_MODULE;
    cdev_init(&newchrled.cdev, &newchrled_fops);

    /* 3、添加一个cdev */
    ret = cdev_add(&newchrled.cdev, newchrled.devid, PLATFORM_CNT);
    if (ret < 0) {
        ret = -ENAVAIL;
        goto fail_error;
    }
    /* 4、创建类 */
    newchrled.class = class_create(THIS_MODULE, PLATFORM_NAME);
    if (IS_ERR(newchrled.class)) {
        return PTR_ERR(newchrled.class);
    }

    /* 5、创建设备 */
    newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, PLATFORM_NAME);
    if (IS_ERR(newchrled.device)) {
        return PTR_ERR(newchrled.device);
    }

    led_init(dev);
    return 0;
    fail_error:
    return ret;

}

static int led_remove(struct platform_device *dev)
{

    printk("led remove is runing\r\n");

    /*1.删除字符设备*/
    cdev_del(&newchrled.cdev);
    /*2.注销设备号*/
    unregister_chrdev_region(newchrled.devid,PLATFORM_CNT);
    /*3.摧毁设备*/
    device_destroy(newchrled.class,newchrled.devid);
    /*4.摧毁类*/
    class_destroy(newchrled.class);
    /*5.释放IO*/
    gpio_set_value(newchrled.led_gpio, 1);
    gpio_free(newchrled.led_gpio);
    return 0;
}
struct of_device_id leds_of_match[] = {
    {
        .compatible = "lalientek-gpioled",
    },
    {/* sentinel */},
};
MODULE_DEVICE_TABLE(of, leds_of_match);
static struct platform_driver leddriver = {
    .driver = {
        .name = "imx6ull-led", /* 驱动名字 */
        .of_match_table = leds_of_match,    /* 设备树中的匹配 */
    },
    .probe = led_probe,   /* probe函数 */
    .remove = led_remove, /* remove函数 */
};



static int __init leddriver_init(void)
{
    int ret = platform_driver_register(&leddriver);
    if(ret < 0) {
        printk("platform_driver_register failed!\r\n");
        return -EFAULT;
    }
    return ret;
}
static void __exit leddriver_exit(void)
{
    platform_driver_unregister(&leddriver);
}
module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wxy");
MODULE_DESCRIPTION("platled driver");
